﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using NPOI.XWPF.UserModel;

// ReSharper disable once CheckNamespace
namespace Tzx.Utilities.Npoi
{

    /// <summary>
    /// 按DataTable数据和Word模板，导出多个Word文件
    /// </summary>
    public class NpoiDocxExport
    {

        #region test

        private static void test()
        {
            List<Dictionary<string, string>> list = new List<Dictionary<string, string>>();

            //
            //Dictionary<string, string> dict = new Dictionary<string, string>();
            //dict["${电话}$"] = "13423433034";
            //dict["${罚款}$"] = "99.88";
            //dict["${交钱}$"] = "200.00";
            //dict["${totalAmt}$"] = "300.00";
            //list.Add(dict);

            DataTable table = new DataTable();
            table.Columns.Add("电话",typeof(string));
            table.Columns.Add("罚款", typeof(string));
            table.Columns.Add("交钱", typeof(string));
            table.Columns.Add("totalAmt", typeof(string));
            DataRow dr = table.NewRow();
            dr["电话"] = "13423433034";
            dr["罚款"] = "99.88";
            dr["交钱"] = "200.00";
            dr["totalAmt"] = "300.00";
            table.Rows.Add(dr);

            //
            string filePath = @"D:\Studio\tzx-codes\用NPOI从Excel取数读Word模板生成多Word文件\Excel2Words\_files_\这是模板2.docx";

            //
            NpoiDocxExport export = new NpoiDocxExport();
            //export.ReplaceDict = dict;
            //export.ReplaceDictList = list;
            export.DataTable_ReplaceExport = table;
            export.DocxTemplateFile = filePath;
            string message;
            bool ok = export.Export(out message);
            if (ok)
            {
                Process.Start(export.GetNewFolderPath());
            }
            else
            {
                MessageBox.Show("失败:" + message);
            }
        }
        #endregion

        #region x

        ///// <summary>
        ///// 替代清单字典
        ///// </summary>
        //[Obsolete("请使用列表",true)]
        //public IDictionary<string, string> ReplaceDict
        //{
        //    get
        //    {
        //        if (_replaceDict == null)
        //        {
        //            _replaceDict = new Dictionary<string, string>();
        //        }
        //        return _replaceDict;
        //    }
        //    set { _replaceDict = value; }
        //}
        //private IDictionary<string, string> _replaceDict = null;

        ///// <summary>
        ///// 替代清单字典
        ///// </summary>
        //[Obsolete("请使用DataTable",true)]
        //public List<Dictionary<string, string>> ReplaceDictList
        //{
        //    get
        //    {
        //        if (_replaceDictList == null)
        //        {
        //            _replaceDictList=new List<Dictionary<string, string>>();
        //        }
        //        return _replaceDictList;
        //    }
        //    set { _replaceDictList = value; }
        //}
        //private List<Dictionary<string, string>> _replaceDictList;

        #endregion

        #region public 事件

        /// <summary>
        /// 导出进度变化时
        /// </summary>
        public event EventHandler OnExportProcess;

        /// <summary>
        /// 
        /// </summary>
        protected virtual void OnExportProcess_Call(int current, int count)
        {
            EventHandler handler = OnExportProcess;
            if (handler != null)
            {
                EventArgs e = new NpoiDocxExportProgressEventArgs(current, count);
                handler(this,e);
            }
        }

        #endregion

        #region public 属性

        /// <summary>
        /// 待替换列名
        /// </summary>
        public HashSet<string> Columns_Replace
        {
            get
            {
                if (_replaceColumnName == null)
                {
                    _replaceColumnName = new HashSet<string>();
                }
                return _replaceColumnName;
            }
            set { _replaceColumnName = value; }
        }
        private HashSet<string> _replaceColumnName;

        /// <summary>
        /// 使用这一列内容来命名文件名
        /// </summary>
        public string Column_FileName
        {
            get { return _columnFileName; }
            set { _columnFileName = value; }
        }
        private string _columnFileName = null;

        /// <summary>
        /// 替代清单字典（表行）
        /// </summary>
        public DataTable DataTable_ReplaceExport
        {
            get
            {
                if (_dataTable == null)
                {
                    _dataTable = new DataTable();
                }
                return _dataTable;
            }
            set { _dataTable = value; }
        }
        private DataTable _dataTable = null;

        /// <summary>
        /// 占位符_开始部分,默认 "${" 开始, "}$" 结束.
        /// </summary>
        public string PlaceHolder_Begin
        {
            get { return _placeHolder_Begin; }
            set { _placeHolder_Begin = value; }
        }
        private string _placeHolder_Begin = "${";

        /// <summary>
        /// 占位符_结束部分,默认 "${" 开始, "}$" 结束.
        /// </summary>
        public string PlaceHolder_End
        {
            get { return _placeHolder_End; }
            set { _placeHolder_End = value; }
        }
        private string _placeHolder_End = "}$";

        /// <summary>
        /// 模板文件路径
        /// </summary>
        public string DocxTemplateFile
        {
            get { return _docxTemplateFile; }
            set { _docxTemplateFile = value; }
        }
        private string _docxTemplateFile = null;

        /// <summary>
        /// 导出到子文件夹名(默认日期时间字符串yyyyMMdd_HHmmss)
        /// </summary>
        public string FolderName
        {
            get
            {
                if (_folderName == null)
                {
                    _folderName = DateTime.Now.ToString("yyyyMMdd_HHmmss");
                }
                return _folderName;
            }
            set
            {
                string v = string.Format("{0}", value).Replace("\\","_").Replace("/","_");
                _folderName = v;
            }
        }
        private string _folderName = null;

        #endregion

        #region public 方法

        /// <summary>
        /// 导出
        /// </summary>
        public bool Export(out string message)
        {
            message = null;
            if (string.IsNullOrEmpty(DocxTemplateFile))
            {
                message = "模板文件路径为空";
                return false;
            }
            if (!File.Exists(DocxTemplateFile))
            {
                message = "模板文件不存在";
                return false;
            }

            //保存文件夹位置
            string savePath = GetNewFolderPath();
            if (!Directory.Exists(savePath))
            {
                Directory.CreateDirectory(savePath);
            }

            //用户传入待替换列,且在表格中存在的
            List<string> existColumn_Replace = new List<string>();
            foreach (string col in Columns_Replace)
            {
                if (DataTable_ReplaceExport.Columns.Contains(col))
                {
                    existColumn_Replace.Add(col);
                }
            }

            //用户传入待作为文件名的列
            bool existColumn_FileName = DataTable_ReplaceExport.Columns.Contains(Column_FileName);

            //模板(克隆流)
            byte[] template;
            using (Stream fs_template = new FileStream(DocxTemplateFile, FileMode.Open, FileAccess.Read))
            {
                template = StreamToBytes(fs_template);
                fs_template.Close(); //关
            } //using

            //遍历Table
            int count = DataTable_ReplaceExport.Rows.Count;
            for (int i = 0; i < count; i++)
            {
                DataRow row = DataTable_ReplaceExport.Rows[i];
                Dictionary<string, string> dict = ReplaceRow_To_Dict(row, existColumn_Replace);
                string fileName = string.Empty;
                if (existColumn_FileName)
                {
                    fileName = string.Format("{0}", row[Column_FileName]).Trim();
                }

                using (Stream ms = BytesToStream(template))
                {
                    XWPFDocument doc2 = new XWPFDocument(ms);

                    //核心1,替换段落里面的变量  
                    Replace_Paragraph(doc2, dict);

                    //核心2,替换表格里面的变量  
                    Replace_Table(doc2, dict);

                    string fileNew = GetNewDocxFullPath(i + 1, count, fileName);
                    using (Stream fs_newfile = new FileStream(fileNew, FileMode.Create, FileAccess.Write))
                    {
                        doc2.Write(fs_newfile);
                        fs_newfile.Close(); //关
                    } //using 导出文件流

                    ms.Close(); //关
                } //using 模板流

                //OnExportProcess
                OnExportProcess_Call(i + 1, count);

            } //for


            //返回
            return true;
        }

        #endregion

        #region private Replace Word

        /// <summary>
        /// 替换段落里面的变量</summary>
        /// <param name="doc">要替换的文档</param>
        /// <param name="params3">参数</param>
        private void Replace_Paragraph(XWPFDocument doc, Dictionary<string, string> params3)
        {
            IEnumerator<XWPFParagraph> iterator = doc.GetParagraphsEnumerator();
            XWPFParagraph para;
            while (iterator.MoveNext())
            {
                para = iterator.Current;
                Replace_Paragraph(para, params3);
            }
        }

        /// <summary>
        /// 替换表格里面的变量
        /// </summary>
        /// <param name="doc">要替换的文档</param>
        /// <param name="params4">参数</param>
        private void Replace_Table(XWPFDocument doc, Dictionary<string, string> params4)
        {
            IEnumerator<XWPFTable> iterator = doc.GetTablesEnumerator();
            XWPFTable table;
            IList<XWPFTableRow> rows;
            IList<XWPFTableCell> cells;
            IList<XWPFParagraph> paras;
            while (iterator.MoveNext())
            {
                table = iterator.Current;
                rows = table.Rows;
                foreach (XWPFTableRow row in rows)
                {
                    cells = row.GetTableCells();
                    foreach (XWPFTableCell cell in cells)
                    {
                        paras = cell.Paragraphs;
                        foreach (XWPFParagraph para in paras)
                        {
                            Replace_Paragraph(para, params4);
                        }//foreach paragraph
                    }//foreach cell
                }//foreach row
            }//while
        }

        /// <summary>
        /// 替换段落里面的变量(关键操作)
        /// </summary>
        /// <param name="para">要替换的段落</param>
        /// <param name="params5">参数</param>
        private void Replace_Paragraph(XWPFParagraph para, Dictionary<string, string> params5)
        {
            foreach (KeyValuePair<string, string> kv in params5)
            {
                string find = kv.Key;
                string replace = kv.Value;

#if DEBUG
                LogFile.Me.LogText("find=", find, "replace=", replace);
#endif

                if (para.Text.Contains(find))
                {
#if DEBUG
                    LogFile.Me.LogText("找到find=", find, "replace=", replace);
#endif
                    para.ReplaceText(find, replace); //关键操作
                }
                else
                {
#if DEBUG
                    LogFile.Me.LogText("没有找到" + find);
#endif                    
                }
            }

            //foreach (string key in params5.Keys)
            //{
            //    if (para.Text.Contains(key))
            //    {
            //        para.ReplaceText(key, params5[key]);
            //    }
            //}

        }

        #endregion

        #region private

        /// <summary>
        /// 表行转字典
        /// </summary>
        /// <param name="row"></param>
        /// <param name="existColumnsReplace"></param>
        /// <returns></returns>
        private Dictionary<string, string> ReplaceRow_To_Dict(DataRow row, IEnumerable<string> existColumnsReplace)
        {
            //DataTable parentTable = row.Table;
            Dictionary<string, string> dict = new Dictionary<string, string>();
            foreach (string col in existColumnsReplace) //Columns_Replace
            {
                //if (parentTable.Columns.Contains(col))
                //{
                string key = string.Format("{0}{1}{2}", PlaceHolder_Begin, col, PlaceHolder_End);
                dict.Add(key, row[col].ToString());
                //}
            }
            return dict;
        }

        /// <summary>
        /// 获取本次导出对象,完整新文件夹路径
        /// </summary>
        /// <returns></returns>
        private string GetNewFolderPath()
        {
            string path1 = string.Format("{0}", Path.GetDirectoryName(DocxTemplateFile));
            string path2 = FolderName;
            return Path.Combine(path1, path2);
        }

        /// <summary>
        /// 获取保存新文件的完整路径名称
        /// </summary>
        /// <param name="index"></param>
        /// <param name="count"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private string GetNewDocxFullPath(int index, int count, string fileName)
        {
            //
            string num = string.Empty;
            int len = (count <= 99) ? 2 : count.ToString().Length;
            num = index.ToString().PadLeft(len, '0') + "_";

            //
            string templatePath = Path.GetDirectoryName(DocxTemplateFile);
            if (string.IsNullOrEmpty(templatePath))
            {
                templatePath = "C:\\";
            }
            string fileNew = string.Format("{0}\\{1}\\{2}{3}.docx",
                templatePath.TrimEnd('\\'),
                FolderName,
                num,
                fileName
                );
            return fileNew;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        private static byte[] StreamToBytes(Stream stream)
        {
            byte[] bytes = new byte[stream.Length];
            stream.Read(bytes, 0, bytes.Length);
            // 设置当前流的位置为流的开始
            stream.Seek(0, SeekOrigin.Begin);
            return bytes;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        private static Stream BytesToStream(byte[] bytes)
        {
            Stream stream = new MemoryStream(bytes);
            return stream;
        }
        #endregion

    }

    /// <summary>
    /// 进度事件参数
    /// </summary>
    public class NpoiDocxExportProgressEventArgs : EventArgs
    {
        public NpoiDocxExportProgressEventArgs(int curentVal,int countVal)
        {
            this.CurrentValue = curentVal;
            this.CountValue = countVal;
        }
        public int CurrentValue { get; private set; }
        public int CountValue { get; private set; }
    }

}